package com.radar.algorithm;

import com.radar.common.GisConstants;
import com.radar.utils.RefObj;
import org.meteoinfo.global.PointD;

import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;

public class TITAN2 {

    public List<List<PointD>> GetRuns(boolean[][] gridz) {
        List<List<PointD>> list = new ArrayList<>();
        for (int i = 0; i < gridz.length; i++) {
            int num = -1;
            List<PointD> list2 = new ArrayList<>();
            for (int j = 0; j < gridz[0].length; j++) {
                if (!gridz[i][j]) {
                    continue;
                }
                if (num == -1) {
                    num = j;
                    list2.add(new PointD(i, j));
                    continue;
                }
                if (j != num + 1) {
                    List<PointD> list3 = new ArrayList<>();
                    for (int k = 0; k < list2.size(); k++) {
                        list3.add(list2.get(k));
                    }
                    list.add(list3);
                    list2.clear();
                }
                num = j;
                list2.add(new PointD(i, j));
            }
            if (list2.size() != 0) {
                list.add(list2);
            }
        }
        return list;
    }

    public List<List<PointD>> PreGetGroups(List<List<PointD>> Runs) {
        int num = 3;
        for (int i = 1; i < Runs.size(); i++) {
            if (Runs.get(i).get(0).X == Runs.get(i - 1).get(0).X && Runs.get(i).get(0).Y - Runs.get(i - 1).get(Runs.get(i - 1).size() - 1).Y <= num) {
                List<PointD> list = new ArrayList<>();
                for (int j = 0; j < Runs.get(i - 1).size(); j++) {
                    list.add(Runs.get(i - 1).get(j));
                }
                for (int j = 1; j < num; j++) {
                    list.add(new PointD(Runs.get(i).get(0).X, Runs.get(i - 1).get(Runs.get(i - 1).size() - 1).Y + j));
                }
                for (int j = 0; j < Runs.get(i).size(); j++) {
                    list.add(Runs.get(i).get(j));
                }
                Runs.add(i - 1, list);
                Runs.remove(i);
                Runs.remove(i);
            }
        }
        return Runs;
    }

    public List<List<List<PointD>>> GetGroups(List<List<PointD>> Runs, int PointsNo) {
        List<List<List<PointD>>> list = new ArrayList<>();
        if (Runs.size() == 0) {
            return list;
        }
        List<List<PointD>> list2 = new ArrayList<>();
        list2.add(Runs.get(0));
        for (int i = 1; i < Runs.size(); i++) {
            boolean flag = false;
            if (Runs.get(i).get(0).X == Runs.get(i - 1).get(0).X + 1) {
                for (int j = 0; j < Runs.get(i).size(); j++) {
                    if(contains(Runs.get(i - 1),new PointD(Runs.get(i).get(j).X - 1, Runs.get(i).get(j).Y))){
                        list2.add(Runs.get(i));
                        flag = true;
                        break;
                    }
                }
            }
            if (!flag) {
                List<List<PointD>> list3 = new ArrayList<>();
                for (int j = 0; j < list2.size(); j++) {
                    list3.add(list2.get(j));
                }
                list.add(list3);
                list2.clear();
                list2.add(Runs.get(i));
            }
        }
        if (list2.size() != 0) {
            List<List<PointD>> list3 = new ArrayList<>();
            for (int j = 0; j < list2.size(); j++) {
                list3.add(list2.get(j));
            }
            list.add(list3);
            list2.clear();
        }
        for (int i = 0; i < list.size() - 1; i++) {
            if (list.get(i).get(list.get(i).size() - 1).get(0).X != list.get(i + 1).get(0).get(0).X || list.get(i + 1).size() <= 1) {
                continue;
            }
            for (int j = 0; j < list.get(i).get(list.get(i).size() - 1).size(); j++) {
                if (contains(list.get(i + 1).get(1),new PointD(list.get(i).get(list.get(i).size() - 1).get(j).X + 1, list.get(i).get(list.get(i).size() - 1).get(j).Y))) {
                    List<List<PointD>> list3 = new ArrayList<>();
                    for (int k = 0; k < list.get(i).size(); k++) {
                        list3.add(list.get(i).get(k));
                    }
                    for (int k = 0; k < list.get(i + 1).size(); k++) {
                        list3.add(list.get(i + 1).get(k));
                    }
                    list.add(i, list3);
                    list.remove(i + 1);
                    list.remove(i + 1);
                    break;
                }
            }
        }
        boolean flag2;
        for (int i = 0; i < list.size(); i++) {
            flag2 = false;
            for (int l = 0; l < list.get(i).get(0).size(); l++) {
                for (int j = 0; j < i; j++) {
                    if (contains(list.get(j).get(list.get(j).size() - 1),new PointD(list.get(i).get(0).get(l).X - 1, list.get(i).get(0).get(l).Y))) {
                        List<List<PointD>> list3 = new ArrayList<>();
                        for (int k = 0; k < list.get(j).size(); k++) {
                            list3.add(list.get(j).get(k));
                        }
                        for (int k = 0; k < list.get(i).size(); k++) {
                            list3.add(list.get(i).get(k));
                        }
                        list.add(j, list3);
                        list.remove(j + 1);
                        list.remove(i);
                        flag2 = true;
                        break;
                    }
                }
                if (flag2) {
                    i = 0;
                    break;
                }
            }
        }
        for (int i = 0; i < list.size(); i++) {
            int num = 0;
            for (int j = 0; j < list.get(i).size(); j++) {
                num += list.get(i).get(j).size();
            }
            if (num < PointsNo) {
                list.remove(i);
                i--;
            }
        }
        return list;
    }

    public List<List<List<PointD>>> GetGroups2(List<List<List<PointD>>> group) {
        for (int i = 0; i < group.size(); i++) {
            boolean flag = false;
            int num = 0;
            for (num = 0; num < group.size(); num++) {
                if (num == i) {
                    continue;
                }
                for (int num2 = group.get(i).size() - 1; num2 >= 0; num2--) {
                    for (int j = 0; j < group.get(i).get(num2).size(); j++) {
                        for (int k = 0; k < group.get(num).size() - 1; k++) {
                            if (contains(group.get(num).get(k),new PointD(group.get(i).get(num2).get(0).X + 1, group.get(i).get(num2).get(j).Y))) {
                                flag = true;
                                break;
                            }
                        }
                        if (flag) {
                            break;
                        }
                    }
                    if (flag) {
                        break;
                    }
                }
                if (flag) {
                    break;
                }
            }
            if (flag) {
                List<List<PointD>> list = new ArrayList<List<PointD>>();
                for (int j = 0; j < group.get(i).size(); j++) {
                    list.add(group.get(i).get(j));
                }
                for (int j = 0; j < group.get(num).size(); j++) {
                    list.add(group.get(num).get(j));
                }
                group.add(i, list);
                group.remove(i + 1);
                group.remove(num);
                i--;
            }
        }
        return group;
    }

    public List<List<List<PointD>>> GetStorms(boolean[][] gridz, int PointDsNo) {
        return GetGroups2(GetGroups(PreGetGroups(GetRuns(gridz)), PointDsNo));
    }

    public List<PointD> GetBoundary(List<List<PointD>> Storm) {
        List<PointD> list = new ArrayList<>();
        for (int i = 0; i < Storm.size(); i++) {
            for (int j = 0; j < Storm.get(i).size(); j++) {
                list.add(Storm.get(i).get(j));
            }
        }
        List<Integer> list2 = new ArrayList<>();
        for (int i = 0; i < list.size(); i++) {
            list2.add((int) list.get(i).Y);
        }
        List<Integer> list3 = new ArrayList<>();
        int num = list2.stream().min(Integer::compareTo).get();
        for (int i = 0; i < list.size(); i++) {
            if (list2.get(i) == num) {
                list3.add((int) list.get(i).X);
            }
        }
        PointD point = new PointD(list3.stream().min(Integer::compareTo).get(), num);
        List<Integer> list4 = new ArrayList<>();
        for (int i = 0; i < list.size(); i++) {
            list4.add((int) list.get(i).Y);
        }
        List<Integer> list5 = new ArrayList<>();
        int num2 = list4.stream().max(Integer::compareTo).get();
        for (int i = 0; i < list.size(); i++) {
            if (list4.get(i) == num2) {
                list5.add((int) list.get(i).X);
            }
        }
        PointD right = new PointD(list5.stream().max(Integer::compareTo).get(), num2);
        PointD[] array = new PointD[]{
                new PointD(-1, 0),
                new PointD(0, 1),
                new PointD(1, 0),
                new PointD(0, -1)
        };
        PointD[] s = new PointD[]{
                new PointD(1, 0),
                new PointD(0, -1),
                new PointD(-1, 0),
                new PointD(0, 1)
        };
        List<PointD> list6 = new ArrayList<>();
        list6.add(point);
        PointD PointD2 = point;
        PointD PointD3 = new PointD(-1, -1);
        boolean flag = false;
        boolean flag2 = false;
        PointD[] array2 = Reverse(s);
//        while (PointD3 != point) {
        while (PointD3.X != point.X ||  PointD3.Y != point.Y) {
            if (!contains(list,point)) {
                point = list6.get(indexOf(list6,point) + 1);
            }
            if (PointD2.X == right.X && PointD2.Y == right.Y) {
                array2 = Reverse(array2);
            }
            if (flag) {
                array2 = Reverse(array2);
                flag = false;
                flag2 = false;
            }
            for (int i = 0; i < 4; i++) {
                PointD3 = new PointD(PointD2.X + array2[i].X, PointD2.Y + array2[i].Y);
//                PointD3 = PointD.add(PointD2, array2[i]);
                if (PointD3.X == point.X && PointD3.Y == point.Y) {
                    break;
                }
                boolean flag3 = Onlycontains(list, PointD2, array2, i);
                if ((contains(list,PointD3) && !contains(list6,PointD3)) || flag3) {
                    list6.add(PointD3);
                    if (flag3) {
                        remove(list,PointD2);
                    }
                    PointD2 = PointD3;
                    if (i == 3 && !flag3) {
                        flag = true;
                        flag2 = true;
                    }
                    if (i == 0 && contains(list,new PointD(PointD2.X + array2[3].X, PointD2.Y + array2[3].Y))) {
                        flag = true;
                        flag2 = true;
                    }
                    break;
                }
                if (flag2 && contains(list6,PointD3)) {
                    PointD item = new PointD(PointD2.X + Reverse(array2)[1].X, PointD2.Y + Reverse(array2)[1].Y);
                    if (contains(list,PointD3) && !contains(list6,item)) {
                        flag = true;
                        flag2 = false;
                    }
                }
                if (i != 3) {
                    continue;
                }
                List<Integer> list7 = new ArrayList<>();
                List<PointD> list8 = new ArrayList<>();
                for (int j = 0; j < 4; j++) {
                    list8.add(new PointD(PointD2.X + array2[j].X, PointD2.Y + array2[j].Y));
                    if (contains(list6,list8.get(j))) {
                        list7.add(indexOf(list6,list8.get(j)));
                    }
                }
                PointD2 = list6.get(list7.stream().min(Integer::compareTo).get());
                PointD3 = PointD2;
                list6.add(PointD3);
                int num3 = indexOf(list8,PointD3);
                if (num3 == 3 && !flag3) {
                    flag = true;
                    flag2 = true;
                }
                if (num3 == 0 && contains(list,new PointD(PointD2.X + array2[3].X, PointD2.Y + array2[3].Y))) {
                    flag = true;
                    flag2 = true;
                }
            }
        }
        list6.add(point);
        return list6;
    }

    public PointD[] Reverse(PointD[] s) {
        PointD[] array = new PointD[]{
                new PointD(-1, 0),
                new PointD(0, 1),
                new PointD(1, 0),
                new PointD(0, -1)
        };
        PointD[] result = new PointD[]{
                new PointD(1, 0),
                new PointD(0, -1),
                new PointD(-1, 0),
                new PointD(0, 1)
        };
        if (s[0].X == array[0].X && s[0].Y == array[0].Y) {
            return result;
        }
        return array;
    }

    public void remove(List<PointD> list, PointD cp){
        for(PointD d : list){
            if(d.X == cp.X && d.Y == cp.Y){
                list.remove(d);
                return;
            }
        }
    }
    public boolean Onlycontains(List<PointD> S, PointD cp, PointD[] Direction, int i) {
        boolean flag = true;
        for (int j = 0; j < 4; j++) {
            if (j != i && contains(S,new PointD(cp.X + Direction[j].X, cp.Y + Direction[j].Y))) {
                flag = false;
                break;
            }
        }

        if (contains(S,new PointD(cp.X + Direction[i].X, cp.Y + Direction[i].Y)) && flag) {
            return true;
        }
        return false;
    }

    public List<PointD> GetAllPoints(List<List<List<PointD>>> storms) {
        List<PointD> list = new ArrayList<PointD>();
        for (int i = 0; i < storms.size(); i++) {
            for (int j = 0; j < storms.get(i).size(); j++) {
                for (int k = 0; k < storms.get(i).get(j).size(); k++) {
                    list.add(storms.get(i).get(j).get(k));
                }
            }
        }
        return list;
    }

    public boolean[][] ErodeBoundary(boolean[][] B, double[][] V, int step, double Tz, List<List<List<PointD>>> storms) {
        double num = Tz + 5.0;
        int num2 = 2 * step * step;
        boolean[][] array = new boolean[B.length][B[0].length];
        for (int i = step; i < B.length - step; i++) {
            for (int j = step; j < B[0].length - step; j++) {
                if (!B[i][j]) {
                    continue;
                }
                array[i][j] = true;
                if (!(V[i][j] < num)) {
                    continue;
                }
                boolean flag = false;
                int num3 = -1;
                for (int k = 0; k < storms.size(); k++) {
                    for (int l = 0; l < storms.get(k).size(); l++) {
                        if (contains(storms.get(k).get(l),new PointD(i, j))) {
                            flag = true;
                            break;
                        }
                    }
                    if (flag) {
                        num3 = k;
                        break;
                    }
                }
                int num4 = 0;
                if (num3 != -1) {
                    num4 = Area(storms.get(num3));
                }
                if (num4 <= num2) {
                    continue;
                }
                int num5 = (int) (1.0 + (num - V[i][j]) * (double) (step - 1) / (num - Tz));
                if (num5 < 0) {
                    num5 = 0;
                }
                for (int k = -1 * num5; k <= num5; k++) {
                    if (i + k >= 0 && i + k < B.length && j + k >= 0 && j + k < B[0].length && (!B[i + k][j] || !B[i][j + k])) {
                        array[i][j] = false;
                        break;
                    }
                }
            }
        }
        return array;
    }

    public PointD MC(List<List<PointD>> storm, double[][] var, RefObj<Double> aveZ) {
        double num = 0.0;
        double num2 = 0.0;
        double num3 = 0.0;
        int num4 = 0;
        for (int i = 0; i < storm.size(); i++) {
            for (int j = 0; j < storm.get(i).size(); j++) {
                if (var[(int) storm.get(i).get(j).X][(int) storm.get(i).get(j).Y] != GisConstants.UNDEF) {
                    num4++;
                    num += var[(int) storm.get(i).get(j).X][(int) storm.get(i).get(j).Y] * (double) storm.get(i).get(j).X;
                    num2 += var[(int) storm.get(i).get(j).X][(int) storm.get(i).get(j).Y] * (double) storm.get(i).get(j).Y;
                    num3 += var[(int) storm.get(i).get(j).X][(int) storm.get(i).get(j).Y];
                }
            }
        }
        int x = (int) (num / num3);
        int y = (int) (num2 / num3);
        aveZ.setValue(num3 / (double) num4);
        return new PointD(x, y);
    }

    public int Area(List<List<PointD>> storm) {
        int num = 0;
        for (int i = 0; i < storm.size(); i++) {
            num += storm.get(i).size();
        }
        return num;
    }

    public void DilateBoundary(List<PointD> lps, boolean[][] V) {
        for (int i = 0; i < lps.size(); i++) {
            if (!V[(int) (lps.get(i).X - 1)][(int) lps.get(i).Y]) {
                V[(int) (lps.get(i).X - 1)][(int) lps.get(i).Y] = true;
            }
            if (!V[(int) (lps.get(i).X + 1)][(int) lps.get(i).Y]) {
                V[(int) (lps.get(i).X + 1)][(int) lps.get(i).Y] = true;
            }
            if (!V[(int) lps.get(i).X][(int) (lps.get(i).Y - 1)]) {
                V[(int) lps.get(i).X][(int) (lps.get(i).Y - 1)] = true;
            }
            if (!V[(int) lps.get(i).X][(int) (lps.get(i).Y + 1)]) {
                V[(int) lps.get(i).X][(int) (lps.get(i).Y + 1)] = true;
            }
        }
    }

    public double BoundaryDistance(List<PointD> lps0, List<PointD> lps1) {
        List<Integer> list = new ArrayList<Integer>();
        for (int i = 0; i < lps0.size(); i++) {
            int x = (int) lps0.get(i).X;
            int y = (int) lps0.get(i).Y;
            for (int j = 0; j < lps1.size(); j++) {
                int x2 = (int) lps1.get(j).X;
                int y2 = (int) lps1.get(j).Y;
                int item = (x2 - x) * (x2 - x) + (y2 - y) * (y2 - y);
                list.add(item);
            }
        }
        return Math.sqrt(list.stream().mapToInt(z -> z.intValue()).min().getAsInt());
    }

    public boolean contains(List<PointD> pointDS ,PointD point){
        for(int i = 0 ; i < pointDS.size() ; i ++){
            if(point.X == pointDS.get(i).X && point.Y == pointDS.get(i).Y){
                return true;
            }
        }
        return false;
    }
    public int indexOf(List<PointD> pointDS ,PointD point){
        for(int i = 0 ; i < pointDS.size() ; i ++){
            if(point.X == pointDS.get(i).X && point.Y == pointDS.get(i).Y){
                return i;
            }
        }
        return -1;
    }
}
